#include "rtc.h"
#include "time.h"
#include "wifi.h"

RtcDS1307<TwoWire> Rtc(Wire);
RtcDateTime ds1338;
RTC rtc;

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "cn.ntp.org.cn", 8*3600, 0);

uint8_t time_set = 0, time_sync = 0, force_update = 0;

const unsigned char day_per_mon[MONTH_PER_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const char *monthName[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

unsigned char isLeapYear(unsigned int year)
{
    if (year % 400 == 0)
        return 1;
    else if (year % 100 == 0)
        return 0;
    else if (year % 4 == 0)
        return 1;
    else
        return 0;
}

unsigned char day_of_month(unsigned char month, unsigned int year)
{
    if (month == 0 || month > 12)
        return day_per_mon[1] + isLeapYear(year);
    if (month != 2)
        return day_per_mon[month - 1];
    else
        return day_per_mon[1] + isLeapYear(year);
}

void date_cal(unsigned long utc_sec)
{
    int day;
    uint y;
    uint m, d;

    day = utc_sec / SEC_PER_DAY;
    for (y = UTC_BASE_YEAR; day > 0; y++)
    {
        d = (DAY_PER_YEAR + isLeapYear(y));
        if (day >= d)
            day -= d;
        else
            break;
    }
    rtc.year = (byte)(y % 100);

    for (m = 1; m < MONTH_PER_YEAR; m++)
    {
        d = day_of_month(m, y);
        if (day >= d)
            day -= d;
        else
            break;
    }

    rtc.month = (byte)m;
    rtc.date = (byte)(day + 1);
}

uint8_t isTimeValid(void)
{
    if(ds1338.Year() > 2199)
        return YEAR_INVALID;
    else if(ds1338.Month() > 12)
        return MONTH_INVALID;
    else if(ds1338.Day() > 31)
        return DATE_INVALID;
    else if(ds1338.DayOfWeek() > 7)
        return WEEK_INVALID;
    else if(ds1338.Hour() > 23)
        return HOUR_INVALID;
    else if(ds1338.Minute() > 59)
        return MIN_INVALILD;
    else if(ds1338.Second() > 59)
        return SEC_INVALID;
    else
        return NONE_INVALID;
}

void timeSync(void)
{
    static uint8_t fail_cnt = 0;
    static uint16_t cnt = 0, force_update = 0;

    if(WIFI_STATE)
    {
        if(cnt%600==0 || force_update==1)
        {
            force_update = 0;
            cnt = 0;
            timeClient.begin();
            // Serial.println("Sync start");
            if(timeClient.update() == false)
            {
                fail_cnt++;
                timeClient.end();
                if(fail_cnt < 10)
                    force_update = 1;
                else
                    fail_cnt = 0;
                Serial.println("time sync failed");
            }
            else
            {
                // Serial.println(timeClient.getFormattedTime());
                date_cal(timeClient.getEpochTime());
                rtc.week = (byte)timeClient.getDay();
                rtc.hour = (byte)timeClient.getHours();
                rtc.min = (byte)timeClient.getMinutes();
                rtc.sec = (byte)timeClient.getSeconds();
                timeClient.end();
                time_set = 1;
            }
        }
        cnt++;
    }
    else
    {
        cnt = 0;
        force_update = 0;
        fail_cnt = 0;
    }
}

void rtcInit(void)
{
    if(!Rtc.GetIsRunning())        
        Rtc.SetIsRunning(true);
}

void rtcTask(void)
{
        String date_str;
        String time_str;
        uint8_t error_val;

        if(time_set)
        {
            time_set = 0;
            date_str = String(monthName[rtc.month - 1]) + " " + String(rtc.date/10) + String(rtc.date%10) + " " + "20" + String(rtc.year);
            time_str = String(rtc.hour/10) + String(rtc.hour%10) + ":" + String(rtc.min/10) + String(rtc.min%10) + ":" + String(rtc.sec/10) + String(rtc.sec%10);
            Serial.print(date_str);
            Serial.println(time_str);
            RtcDateTime time_set = RtcDateTime(date_str.c_str(), time_str.c_str());
            Rtc.SetDateTime(time_set);
        }

        do
        {
            ds1338 = Rtc.GetDateTime();    
        } while (isTimeValid());

        rtc.year = ds1338.Year();
        rtc.month = ds1338.Month();
        rtc.date = ds1338.Day();
        rtc.week = ds1338.DayOfWeek();
        rtc.hour = ds1338.Hour();
        rtc.min = ds1338.Minute();
        rtc.sec = ds1338.Second();
        // Serial.printf("week:%d\r\n", rtc.week);
        // Serial.printf("Time:%d.%d.%d %.2d:%.2d:%.2d\r\n", ds1338.Year(), ds1338.Month(), ds1338.Day(), ds1338.Hour(), ds1338.Minute(), ds1338.Second());
}
